package com.duguo.dynamicmvc.factory.processor.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javassist.CtClass;
import javassist.CtMethod;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

import com.duguo.dynamicmvc.factory.ControllerBuilder;
import com.duguo.dynamicmvc.factory.ControllerFactory;
import com.duguo.dynamicmvc.factory.interceptor.InterceptorGenerator;
import com.duguo.dynamicmvc.factory.utils.BeanFactoryUtils;
import com.duguo.dynamicmvc.factory.utils.ChangeOnceBoolean;


public class ControllerInterceptorCreatorImpl implements BeanFactoryAware,ControllerInterceptorCreator{

	ControllerNameBuilder _controllerNameBuilder;
    BeanFactory _beanFactory;
	
    public String addInterceptors(CtClass controllerCtClass, CtMethod controllerMethodCtMethod,String realCommand,ChangeOnceBoolean hasValidationException, ControllerBuilder controllerBuilder) throws Exception {
    	StringBuffer methodBody = new StringBuffer(realCommand);
    	String controllerName=_controllerNameBuilder.getControllerPath(controllerCtClass.getName());
    	List<String> interceptorAnnotation=controllerBuilder.getProcessorHolder().getViewProcessor().getInterceptors(controllerMethodCtMethod);
    	List<String> usedInterceptors=new ArrayList<String>();
		addInterceptors(_controllerNameBuilder.getParentLevelName(controllerName),controllerName,usedInterceptors, interceptorAnnotation,methodBody,hasValidationException);
    	if(interceptorAnnotation.size()>0){
    		throw new RuntimeException("Cannot find the intercptor "+interceptorAnnotation.toString()+" for "+controllerName);
    	}
		return methodBody.toString();
    }

	private void addInterceptors(String controllerPath,String controllerName,List<String> usedInterceptors, List<String> interceptorAnnotation, StringBuffer methodBody,ChangeOnceBoolean hasValidationException)  throws Exception{
		Map<String,InterceptorGenerator> interceptorGenerators;
		if(controllerPath.length()>0){
			interceptorGenerators=getInterceptors(controllerPath);
		}else{
			interceptorGenerators=getGloabbleInterceptors();
		}
		if(interceptorGenerators!=null && interceptorGenerators.size()>0){
			for(String intercepterName:interceptorGenerators.keySet()){
				InterceptorGenerator interceptorGenerator=interceptorGenerators.get(intercepterName);
				if(!usedInterceptors.contains(intercepterName) && (isAnnotationInterceptor(interceptorAnnotation,intercepterName) || isMatch(controllerName, interceptorGenerator))){
					if(interceptorGenerator.getPreInvokeCode()!=null || interceptorGenerator.getPostInvokeCode()!=null){
						usedInterceptors.add(intercepterName);
						hasValidationException.setValue(interceptorGenerator.hasValidationException());
						if(interceptorGenerator.getPreInvokeCode()!=null)
							methodBody.insert(0,interceptorGenerator.getPreInvokeCode());
						if(interceptorGenerator.getPostInvokeCode()!=null)
							methodBody.append(interceptorGenerator.getPostInvokeCode());
					}else{
						interceptorAnnotation.add(intercepterName);
					}
				}
			}
		}
		if(controllerPath.length()>1){
			addInterceptors(_controllerNameBuilder.getParentLevelName(controllerPath),controllerName, usedInterceptors, interceptorAnnotation,methodBody,hasValidationException);
		}else if(controllerPath.length()==1){
			addInterceptors("",controllerName, usedInterceptors, interceptorAnnotation,methodBody,hasValidationException);
		}
	}

	private boolean isAnnotationInterceptor(List<String> interceptorAnnotation,String intercepterName) {		
		return interceptorAnnotation.remove(intercepterName);
	}

	private boolean isMatch(String controllerName, InterceptorGenerator interceptorGenerator) {
		if(isInclude(controllerName, interceptorGenerator))
			return !isExclude(controllerName, interceptorGenerator);
		return false;
	}

	private boolean isInclude(String controllerName, InterceptorGenerator interceptorGenerator) {
		if(interceptorGenerator.getInclude().equals("update"))
			return _controllerNameBuilder.isDo(controllerName);
		else if(interceptorGenerator.getInclude().equals("none-update"))
			return !_controllerNameBuilder.isDo(controllerName);
		else if(interceptorGenerator.getInclude().equals("none"))
			return false;
		else{
	        if (controllerName.endsWith("/")) {
	            controllerName = controllerName.substring(0, controllerName.length() - 1);
	        }
			return controllerName.matches(interceptorGenerator.getInclude());
		}
	}

	private boolean isExclude(String controllerName, InterceptorGenerator interceptorGenerator) {
		if(interceptorGenerator.getExclude().equals("update"))
			return _controllerNameBuilder.isDo(controllerName);
		else if(interceptorGenerator.getExclude().equals("none-update"))
			return !_controllerNameBuilder.isDo(controllerName);
		else if(interceptorGenerator.getExclude().length()==0)
			return false;
		else{
	        if (controllerName.endsWith("/")) {
	            controllerName = controllerName.substring(0, controllerName.length() - 1);
	        }
	        return controllerName.matches(interceptorGenerator.getExclude());
		}
	}

	private Map<String, InterceptorGenerator> getInterceptors(String controllerPath) {
		try{
			ControllerFactory controllerFactory=(ControllerFactory)_beanFactory.getBean("&"+controllerPath);
			return controllerFactory.getInterceptorGenerators();
		}catch(Exception ex){
			return null;
		}
	}

	private Map<String, InterceptorGenerator> getGloabbleInterceptors() {
		try{
			ControllerBuilder controllerBuilder=BeanFactoryUtils.getBeanOfType(_beanFactory,ControllerBuilder.class);
			return controllerBuilder.getGloableInterceptorGenerators();
		}catch(Exception ex){
			return null;
		}
	}

	public ControllerNameBuilder getControllerNameBuilder() {
		return _controllerNameBuilder;
	}

	public void setControllerNameBuilder(ControllerNameBuilder controllerNameBuilder) {
		_controllerNameBuilder = controllerNameBuilder;
	}

	public void setBeanFactory(BeanFactory beanFactory) {
		_beanFactory = beanFactory;
	}
    
}
